﻿using System;
using System.Collections.Generic;
using System.Text;

namespace MLForgeSharp.Models.SupervisedLearningModels.NeuralNet
{
    /// <summary>
    /// 人工神经网络
    /// </summary>
    public class ArtiNeuralNet
    {
        private int inputSize; // 输入层大小
        private int hiddenSize; // 隐藏层大小
        private int outputSize; // 输出层大小
        private double[,] weightsInputHidden; // 输入层到隐藏层的权重
        private double[,] weightsHiddenOutput; // 隐藏层到输出层的权重
        private double learningRate; // 学习率

        public ArtiNeuralNet(int inputSize, int hiddenSize, int outputSize, double learningRate = 0.1)
        {
            this.inputSize = inputSize;
            this.hiddenSize = hiddenSize;
            this.outputSize = outputSize;
            this.learningRate = learningRate;

            // 初始化权重
            Random rand = new Random();
            weightsInputHidden = new double[inputSize, hiddenSize];
            weightsHiddenOutput = new double[hiddenSize, outputSize];

            for (int i = 0; i < inputSize; i++)
            {
                for (int j = 0; j < hiddenSize; j++)
                {
                    weightsInputHidden[i, j] = rand.NextDouble() - 0.5;
                }
            }

            for (int i = 0; i < hiddenSize; i++)
            {
                for (int j = 0; j < outputSize; j++)
                {
                    weightsHiddenOutput[i, j] = rand.NextDouble() - 0.5;
                }
            }
        }

        // Sigmoid 激活函数
        private double Sigmoid(double x)
        {
            return 1.0 / (1.0 + Math.Exp(-x));
        }

        // Sigmoid 的导数
        private double SigmoidDerivative(double x)
        {
            return x * (1 - x);
        }

        // 前向传播
        private double[] Forward(double[] inputs)
        {
            double[] hiddenOutputs = new double[hiddenSize];
            double[] finalOutputs = new double[outputSize];

            // 计算隐藏层输出
            for (int j = 0; j < hiddenSize; j++)
            {
                double sum = 0.0;
                for (int i = 0; i < inputSize; i++)
                {
                    sum += inputs[i] * weightsInputHidden[i, j];
                }
                hiddenOutputs[j] = Sigmoid(sum);
            }

            // 计算输出层输出
            for (int j = 0; j < outputSize; j++)
            {
                double sum = 0.0;
                for (int i = 0; i < hiddenSize; i++)
                {
                    sum += hiddenOutputs[i] * weightsHiddenOutput[i, j];
                }
                finalOutputs[j] = Sigmoid(sum);
            }

            return finalOutputs;
        }

        // 训练模型
        public void Train(double[][] inputs, double[][] targets, int epochs = 1000)
        {
            for (int epoch = 0; epoch < epochs; epoch++)
            {
                for (int n = 0; n < inputs.Length; n++)
                {
                    double[] input = inputs[n];
                    double[] target = targets[n];

                    // 前向传播
                    double[] hiddenOutputs = new double[hiddenSize];
                    double[] finalOutputs = new double[outputSize];

                    for (int j = 0; j < hiddenSize; j++)
                    {
                        double sum = 0.0;
                        for (int i = 0; i < inputSize; i++)
                        {
                            sum += input[i] * weightsInputHidden[i, j];
                        }
                        hiddenOutputs[j] = Sigmoid(sum);
                    }

                    for (int j = 0; j < outputSize; j++)
                    {
                        double sum = 0.0;
                        for (int i = 0; i < hiddenSize; i++)
                        {
                            sum += hiddenOutputs[i] * weightsHiddenOutput[i, j];
                        }
                        finalOutputs[j] = Sigmoid(sum);
                    }

                    // 反向传播
                    double[] outputErrors = new double[outputSize];
                    for (int j = 0; j < outputSize; j++)
                    {
                        outputErrors[j] = target[j] - finalOutputs[j];
                    }

                    double[] hiddenErrors = new double[hiddenSize];
                    for (int j = 0; j < hiddenSize; j++)
                    {
                        double error = 0.0;
                        for (int k = 0; k < outputSize; k++)
                        {
                            error += outputErrors[k] * weightsHiddenOutput[j, k];
                        }
                        hiddenErrors[j] = error;
                    }

                    // 更新隐藏层到输出层的权重
                    for (int j = 0; j < hiddenSize; j++)
                    {
                        for (int k = 0; k < outputSize; k++)
                        {
                            weightsHiddenOutput[j, k] += learningRate * outputErrors[k] * SigmoidDerivative(finalOutputs[k]) * hiddenOutputs[j];
                        }
                    }

                    // 更新输入层到隐藏层的权重
                    for (int i = 0; i < inputSize; i++)
                    {
                        for (int j = 0; j < hiddenSize; j++)
                        {
                            weightsInputHidden[i, j] += learningRate * hiddenErrors[j] * SigmoidDerivative(hiddenOutputs[j]) * input[i];
                        }
                    }
                }
            }
        }

        // 预测
        public double[] Predict(double[] inputs)
        {
            return Forward(inputs);
        }
    }

    // 示例程序
    public class ArtiNeuralNetExample
    {
        public ArtiNeuralNetExample()
        {
            // 示例数据
            double[][] inputs = new double[][]
        {
            new double[] { 0, 0 },
            new double[] { 0, 1 },
            new double[] { 1, 0 },
            new double[] { 1, 1 }
        };

            double[][] targets = new double[][]
        {
            new double[] { 0 },
            new double[] { 1 },
            new double[] { 1 },
            new double[] { 0 }
        };

            // 创建模型
            ArtiNeuralNet model = new ArtiNeuralNet(inputSize: 2, hiddenSize: 4, outputSize: 1, learningRate: 0.1);

            // 训练模型
            model.Train(inputs, targets, epochs: 10000);

            // 预测
            double[] testInput = { 1, 0 };
            double[] predictedOutput = model.Predict(testInput);

            System.Console.WriteLine("预测输出: " + predictedOutput[0]);
        }
    }
}
